/*
 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.util;

import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;

/**
 * Static classes and methods for operating on or creating instances of
 * {@link Spliterator} and its primitive specializations
 * {@link Spliterator.OfInt}, {@link Spliterator.OfLong}, and
 * {@link Spliterator.OfDouble}.
 *
 * @see Spliterator
 * @since 1.8
 */
public final class Spliterators {

  // Suppresses default constructor, ensuring non-instantiability.
  private Spliterators() {
  }

  // Empty spliterators

  /**
   * Creates an empty {@code Spliterator}
   *
   * <p>The empty spliterator reports {@link Spliterator#SIZED} and
   * {@link Spliterator#SUBSIZED}.  Calls to
   * {@link java.util.Spliterator#trySplit()} always return {@code null}.
   *
   * @param <T> Type of elements
   * @return An empty spliterator
   */
  @SuppressWarnings("unchecked")
  public static <T> Spliterator<T> emptySpliterator() {
    return (Spliterator<T>) EMPTY_SPLITERATOR;
  }

  private static final Spliterator<Object> EMPTY_SPLITERATOR =
      new EmptySpliterator.OfRef<>();

  /**
   * Creates an empty {@code Spliterator.OfInt}
   *
   * <p>The empty spliterator reports {@link Spliterator#SIZED} and
   * {@link Spliterator#SUBSIZED}.  Calls to
   * {@link java.util.Spliterator#trySplit()} always return {@code null}.
   *
   * @return An empty spliterator
   */
  public static Spliterator.OfInt emptyIntSpliterator() {
    return EMPTY_INT_SPLITERATOR;
  }

  private static final Spliterator.OfInt EMPTY_INT_SPLITERATOR =
      new EmptySpliterator.OfInt();

  /**
   * Creates an empty {@code Spliterator.OfLong}
   *
   * <p>The empty spliterator reports {@link Spliterator#SIZED} and
   * {@link Spliterator#SUBSIZED}.  Calls to
   * {@link java.util.Spliterator#trySplit()} always return {@code null}.
   *
   * @return An empty spliterator
   */
  public static Spliterator.OfLong emptyLongSpliterator() {
    return EMPTY_LONG_SPLITERATOR;
  }

  private static final Spliterator.OfLong EMPTY_LONG_SPLITERATOR =
      new EmptySpliterator.OfLong();

  /**
   * Creates an empty {@code Spliterator.OfDouble}
   *
   * <p>The empty spliterator reports {@link Spliterator#SIZED} and
   * {@link Spliterator#SUBSIZED}.  Calls to
   * {@link java.util.Spliterator#trySplit()} always return {@code null}.
   *
   * @return An empty spliterator
   */
  public static Spliterator.OfDouble emptyDoubleSpliterator() {
    return EMPTY_DOUBLE_SPLITERATOR;
  }

  private static final Spliterator.OfDouble EMPTY_DOUBLE_SPLITERATOR =
      new EmptySpliterator.OfDouble();

  // Array-based spliterators

  /**
   * Creates a {@code Spliterator} covering the elements of a given array,
   * using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(Object[])}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report; it is common to
   * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
   *
   * @param <T> Type of elements
   * @param array The array, assumed to be unmodified during use
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @see Arrays#spliterator(Object[])
   */
  public static <T> Spliterator<T> spliterator(Object[] array,
      int additionalCharacteristics) {
    return new ArraySpliterator<>(Objects.requireNonNull(array),
        additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator} covering a range of elements of a given
   * array, using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(Object[])}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report; it is common to
   * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
   *
   * @param <T> Type of elements
   * @param array The array, assumed to be unmodified during use
   * @param fromIndex The least index (inclusive) to cover
   * @param toIndex One past the greatest index to cover
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, {@code toIndex} is
   * less than {@code fromIndex}, or {@code toIndex} is greater than the array size
   * @see Arrays#spliterator(Object[], int, int)
   */
  public static <T> Spliterator<T> spliterator(Object[] array, int fromIndex, int toIndex,
      int additionalCharacteristics) {
    checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
    return new ArraySpliterator<>(array, fromIndex, toIndex, additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator.OfInt} covering the elements of a given array,
   * using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(int[])}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report; it is common to
   * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
   *
   * @param array The array, assumed to be unmodified during use
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @see Arrays#spliterator(int[])
   */
  public static Spliterator.OfInt spliterator(int[] array,
      int additionalCharacteristics) {
    return new IntArraySpliterator(Objects.requireNonNull(array), additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator.OfInt} covering a range of elements of a
   * given array, using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(int[], int, int)}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report; it is common to
   * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
   *
   * @param array The array, assumed to be unmodified during use
   * @param fromIndex The least index (inclusive) to cover
   * @param toIndex One past the greatest index to cover
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, {@code toIndex} is
   * less than {@code fromIndex}, or {@code toIndex} is greater than the array size
   * @see Arrays#spliterator(int[], int, int)
   */
  public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex,
      int additionalCharacteristics) {
    checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
    return new IntArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator.OfLong} covering the elements of a given array,
   * using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(long[])}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report; it is common to
   * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
   *
   * @param array The array, assumed to be unmodified during use
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @see Arrays#spliterator(long[])
   */
  public static Spliterator.OfLong spliterator(long[] array,
      int additionalCharacteristics) {
    return new LongArraySpliterator(Objects.requireNonNull(array), additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator.OfLong} covering a range of elements of a
   * given array, using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(long[], int, int)}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report.  (For example, if it is
   * known the array will not be further modified, specify {@code IMMUTABLE};
   * if the array data is considered to have an an encounter order, specify
   * {@code ORDERED}).  The method {@link Arrays#spliterator(long[], int, int)} can
   * often be used instead, which returns a spliterator that reports
   * {@code SIZED}, {@code SUBSIZED}, {@code IMMUTABLE}, and {@code ORDERED}.
   *
   * @param array The array, assumed to be unmodified during use
   * @param fromIndex The least index (inclusive) to cover
   * @param toIndex One past the greatest index to cover
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, {@code toIndex} is
   * less than {@code fromIndex}, or {@code toIndex} is greater than the array size
   * @see Arrays#spliterator(long[], int, int)
   */
  public static Spliterator.OfLong spliterator(long[] array, int fromIndex, int toIndex,
      int additionalCharacteristics) {
    checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
    return new LongArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator.OfDouble} covering the elements of a given array,
   * using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(double[])}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report; it is common to
   * additionally specify {@code IMMUTABLE} and {@code ORDERED}.
   *
   * @param array The array, assumed to be unmodified during use
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @see Arrays#spliterator(double[])
   */
  public static Spliterator.OfDouble spliterator(double[] array,
      int additionalCharacteristics) {
    return new DoubleArraySpliterator(Objects.requireNonNull(array), additionalCharacteristics);
  }

  /**
   * Creates a {@code Spliterator.OfDouble} covering a range of elements of a
   * given array, using a customized set of spliterator characteristics.
   *
   * <p>This method is provided as an implementation convenience for
   * Spliterators which store portions of their elements in arrays, and need
   * fine control over Spliterator characteristics.  Most other situations in
   * which a Spliterator for an array is needed should use
   * {@link Arrays#spliterator(double[], int, int)}.
   *
   * <p>The returned spliterator always reports the characteristics
   * {@code SIZED} and {@code SUBSIZED}.  The caller may provide additional
   * characteristics for the spliterator to report.  (For example, if it is
   * known the array will not be further modified, specify {@code IMMUTABLE};
   * if the array data is considered to have an an encounter order, specify
   * {@code ORDERED}).  The method {@link Arrays#spliterator(long[], int, int)} can
   * often be used instead, which returns a spliterator that reports
   * {@code SIZED}, {@code SUBSIZED}, {@code IMMUTABLE}, and {@code ORDERED}.
   *
   * @param array The array, assumed to be unmodified during use
   * @param fromIndex The least index (inclusive) to cover
   * @param toIndex One past the greatest index to cover
   * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
   * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
   * @return A spliterator for an array
   * @throws NullPointerException if the given array is {@code null}
   * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, {@code toIndex} is
   * less than {@code fromIndex}, or {@code toIndex} is greater than the array size
   * @see Arrays#spliterator(double[], int, int)
   */
  public static Spliterator.OfDouble spliterator(double[] array, int fromIndex, int toIndex,
      int additionalCharacteristics) {
    checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
    return new DoubleArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
  }

  /**
   * Validate inclusive start index and exclusive end index against the length
   * of an array.
   *
   * @param arrayLength The length of the array
   * @param origin The inclusive start index
   * @param fence The exclusive end index
   * @throws ArrayIndexOutOfBoundsException if the start index is greater than the end index, if the
   * start index is negative, or the end index is greater than the array length
   */
  private static void checkFromToBounds(int arrayLength, int origin, int fence) {
    if (origin > fence) {
      throw new ArrayIndexOutOfBoundsException(
          "origin(" + origin + ") > fence(" + fence + ")");
    }
    if (origin < 0) {
      throw new ArrayIndexOutOfBoundsException(origin);
    }
    if (fence > arrayLength) {
      throw new ArrayIndexOutOfBoundsException(fence);
    }
  }

  // Iterator-based spliterators

  /**
   * Creates a {@code Spliterator} using the given collection's
   * {@link java.util.Collection#iterator()} as the source of elements, and
   * reporting its {@link java.util.Collection#size()} as its initial size.
   *
   * <p>The spliterator is
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the collection's iterator, and
   * implements {@code trySplit} to permit limited parallelism.
   *
   * @param <T> Type of elements
   * @param c The collection
   * @param characteristics Characteristics of this spliterator's source or elements.  The
   * characteristics {@code SIZED} and {@code SUBSIZED} are additionally reported unless {@code
   * CONCURRENT} is supplied.
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given collection is {@code null}
   */
  public static <T> Spliterator<T> spliterator(Collection<? extends T> c,
      int characteristics) {
    return new IteratorSpliterator<>(Objects.requireNonNull(c),
        characteristics);
  }

  /**
   * Creates a {@code Spliterator} using a given {@code Iterator}
   * as the source of elements, and with a given initially reported size.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned, or the initially reported
   * size is not equal to the actual number of elements in the source.
   *
   * @param <T> Type of elements
   * @param iterator The iterator for the source
   * @param size The number of elements in the source, to be reported as initial {@code
   * estimateSize}
   * @param characteristics Characteristics of this spliterator's source or elements.  The
   * characteristics {@code SIZED} and {@code SUBSIZED} are additionally reported unless {@code
   * CONCURRENT} is supplied.
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static <T> Spliterator<T> spliterator(Iterator<? extends T> iterator,
      long size,
      int characteristics) {
    return new IteratorSpliterator<>(Objects.requireNonNull(iterator), size,
        characteristics);
  }

  /**
   * Creates a {@code Spliterator} using a given {@code Iterator}
   * as the source of elements, with no initial size estimate.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned.
   *
   * @param <T> Type of elements
   * @param iterator The iterator for the source
   * @param characteristics Characteristics of this spliterator's source or elements ({@code SIZED}
   * and {@code SUBSIZED}, if supplied, are ignored and are not reported.)
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static <T> Spliterator<T> spliteratorUnknownSize(Iterator<? extends T> iterator,
      int characteristics) {
    return new IteratorSpliterator<>(Objects.requireNonNull(iterator), characteristics);
  }

  /**
   * Creates a {@code Spliterator.OfInt} using a given
   * {@code IntStream.IntIterator} as the source of elements, and with a given
   * initially reported size.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned, or the initially reported
   * size is not equal to the actual number of elements in the source.
   *
   * @param iterator The iterator for the source
   * @param size The number of elements in the source, to be reported as initial {@code
   * estimateSize}.
   * @param characteristics Characteristics of this spliterator's source or elements.  The
   * characteristics {@code SIZED} and {@code SUBSIZED} are additionally reported unless {@code
   * CONCURRENT} is supplied.
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static Spliterator.OfInt spliterator(PrimitiveIterator.OfInt iterator,
      long size,
      int characteristics) {
    return new IntIteratorSpliterator(Objects.requireNonNull(iterator),
        size, characteristics);
  }

  /**
   * Creates a {@code Spliterator.OfInt} using a given
   * {@code IntStream.IntIterator} as the source of elements, with no initial
   * size estimate.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned.
   *
   * @param iterator The iterator for the source
   * @param characteristics Characteristics of this spliterator's source or elements ({@code SIZED}
   * and {@code SUBSIZED}, if supplied, are ignored and are not reported.)
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static Spliterator.OfInt spliteratorUnknownSize(PrimitiveIterator.OfInt iterator,
      int characteristics) {
    return new IntIteratorSpliterator(Objects.requireNonNull(iterator), characteristics);
  }

  /**
   * Creates a {@code Spliterator.OfLong} using a given
   * {@code LongStream.LongIterator} as the source of elements, and with a
   * given initially reported size.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned, or the initially reported
   * size is not equal to the actual number of elements in the source.
   *
   * @param iterator The iterator for the source
   * @param size The number of elements in the source, to be reported as initial {@code
   * estimateSize}.
   * @param characteristics Characteristics of this spliterator's source or elements.  The
   * characteristics {@code SIZED} and {@code SUBSIZED} are additionally reported unless {@code
   * CONCURRENT} is supplied.
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static Spliterator.OfLong spliterator(PrimitiveIterator.OfLong iterator,
      long size,
      int characteristics) {
    return new LongIteratorSpliterator(Objects.requireNonNull(iterator),
        size, characteristics);
  }

  /**
   * Creates a {@code Spliterator.OfLong} using a given
   * {@code LongStream.LongIterator} as the source of elements, with no
   * initial size estimate.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned.
   *
   * @param iterator The iterator for the source
   * @param characteristics Characteristics of this spliterator's source or elements ({@code SIZED}
   * and {@code SUBSIZED}, if supplied, are ignored and are not reported.)
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static Spliterator.OfLong spliteratorUnknownSize(PrimitiveIterator.OfLong iterator,
      int characteristics) {
    return new LongIteratorSpliterator(Objects.requireNonNull(iterator), characteristics);
  }

  /**
   * Creates a {@code Spliterator.OfDouble} using a given
   * {@code DoubleStream.DoubleIterator} as the source of elements, and with a
   * given initially reported size.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned, or the initially reported
   * size is not equal to the actual number of elements in the source.
   *
   * @param iterator The iterator for the source
   * @param size The number of elements in the source, to be reported as initial {@code
   * estimateSize}
   * @param characteristics Characteristics of this spliterator's source or elements.  The
   * characteristics {@code SIZED} and {@code SUBSIZED} are additionally reported unless {@code
   * CONCURRENT} is supplied.
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static Spliterator.OfDouble spliterator(PrimitiveIterator.OfDouble iterator,
      long size,
      int characteristics) {
    return new DoubleIteratorSpliterator(Objects.requireNonNull(iterator),
        size, characteristics);
  }

  /**
   * Creates a {@code Spliterator.OfDouble} using a given
   * {@code DoubleStream.DoubleIterator} as the source of elements, with no
   * initial size estimate.
   *
   * <p>The spliterator is not
   * <em><a href="Spliterator.html#binding">late-binding</a></em>, inherits
   * the <em>fail-fast</em> properties of the iterator, and implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>Traversal of elements should be accomplished through the spliterator.
   * The behaviour of splitting and traversal is undefined if the iterator is
   * operated on after the spliterator is returned.
   *
   * @param iterator The iterator for the source
   * @param characteristics Characteristics of this spliterator's source or elements ({@code SIZED}
   * and {@code SUBSIZED}, if supplied, are ignored and are not reported.)
   * @return A spliterator from an iterator
   * @throws NullPointerException if the given iterator is {@code null}
   */
  public static Spliterator.OfDouble spliteratorUnknownSize(PrimitiveIterator.OfDouble iterator,
      int characteristics) {
    return new DoubleIteratorSpliterator(Objects.requireNonNull(iterator), characteristics);
  }

  // Iterators from Spliterators

  /**
   * Creates an {@code Iterator} from a {@code Spliterator}.
   *
   * <p>Traversal of elements should be accomplished through the iterator.
   * The behaviour of traversal is undefined if the spliterator is operated
   * after the iterator is returned.
   *
   * @param <T> Type of elements
   * @param spliterator The spliterator
   * @return An iterator
   * @throws NullPointerException if the given spliterator is {@code null}
   */
  public static <T> Iterator<T> iterator(Spliterator<? extends T> spliterator) {
    Objects.requireNonNull(spliterator);
    class Adapter implements Iterator<T>, Consumer<T> {

      boolean valueReady = false;
      T nextElement;

      @Override
      public void accept(T t) {
        valueReady = true;
        nextElement = t;
      }

      @Override
      public boolean hasNext() {
        if (!valueReady) {
          spliterator.tryAdvance(this);
        }
        return valueReady;
      }

      @Override
      public T next() {
        if (!valueReady && !hasNext()) {
          throw new NoSuchElementException();
        } else {
          valueReady = false;
          return nextElement;
        }
      }
    }

    return new Adapter();
  }

  /**
   * Creates an {@code PrimitiveIterator.OfInt} from a
   * {@code Spliterator.OfInt}.
   *
   * <p>Traversal of elements should be accomplished through the iterator.
   * The behaviour of traversal is undefined if the spliterator is operated
   * after the iterator is returned.
   *
   * @param spliterator The spliterator
   * @return An iterator
   * @throws NullPointerException if the given spliterator is {@code null}
   */
  public static PrimitiveIterator.OfInt iterator(Spliterator.OfInt spliterator) {
    Objects.requireNonNull(spliterator);
    class Adapter implements PrimitiveIterator.OfInt, IntConsumer {

      boolean valueReady = false;
      int nextElement;

      @Override
      public void accept(int t) {
        valueReady = true;
        nextElement = t;
      }

      @Override
      public boolean hasNext() {
        if (!valueReady) {
          spliterator.tryAdvance(this);
        }
        return valueReady;
      }

      @Override
      public int nextInt() {
        if (!valueReady && !hasNext()) {
          throw new NoSuchElementException();
        } else {
          valueReady = false;
          return nextElement;
        }
      }
    }

    return new Adapter();
  }

  /**
   * Creates an {@code PrimitiveIterator.OfLong} from a
   * {@code Spliterator.OfLong}.
   *
   * <p>Traversal of elements should be accomplished through the iterator.
   * The behaviour of traversal is undefined if the spliterator is operated
   * after the iterator is returned.
   *
   * @param spliterator The spliterator
   * @return An iterator
   * @throws NullPointerException if the given spliterator is {@code null}
   */
  public static PrimitiveIterator.OfLong iterator(Spliterator.OfLong spliterator) {
    Objects.requireNonNull(spliterator);
    class Adapter implements PrimitiveIterator.OfLong, LongConsumer {

      boolean valueReady = false;
      long nextElement;

      @Override
      public void accept(long t) {
        valueReady = true;
        nextElement = t;
      }

      @Override
      public boolean hasNext() {
        if (!valueReady) {
          spliterator.tryAdvance(this);
        }
        return valueReady;
      }

      @Override
      public long nextLong() {
        if (!valueReady && !hasNext()) {
          throw new NoSuchElementException();
        } else {
          valueReady = false;
          return nextElement;
        }
      }
    }

    return new Adapter();
  }

  /**
   * Creates an {@code PrimitiveIterator.OfDouble} from a
   * {@code Spliterator.OfDouble}.
   *
   * <p>Traversal of elements should be accomplished through the iterator.
   * The behaviour of traversal is undefined if the spliterator is operated
   * after the iterator is returned.
   *
   * @param spliterator The spliterator
   * @return An iterator
   * @throws NullPointerException if the given spliterator is {@code null}
   */
  public static PrimitiveIterator.OfDouble iterator(Spliterator.OfDouble spliterator) {
    Objects.requireNonNull(spliterator);
    class Adapter implements PrimitiveIterator.OfDouble, DoubleConsumer {

      boolean valueReady = false;
      double nextElement;

      @Override
      public void accept(double t) {
        valueReady = true;
        nextElement = t;
      }

      @Override
      public boolean hasNext() {
        if (!valueReady) {
          spliterator.tryAdvance(this);
        }
        return valueReady;
      }

      @Override
      public double nextDouble() {
        if (!valueReady && !hasNext()) {
          throw new NoSuchElementException();
        } else {
          valueReady = false;
          return nextElement;
        }
      }
    }

    return new Adapter();
  }

  // Implementations

  private static abstract class EmptySpliterator<T, S extends Spliterator<T>, C> {

    EmptySpliterator() {
    }

    public S trySplit() {
      return null;
    }

    public boolean tryAdvance(C consumer) {
      Objects.requireNonNull(consumer);
      return false;
    }

    public void forEachRemaining(C consumer) {
      Objects.requireNonNull(consumer);
    }

    public long estimateSize() {
      return 0;
    }

    public int characteristics() {
      return Spliterator.SIZED | Spliterator.SUBSIZED;
    }

    private static final class OfRef<T>
        extends EmptySpliterator<T, Spliterator<T>, Consumer<? super T>>
        implements Spliterator<T> {

      OfRef() {
      }
    }

    private static final class OfInt
        extends EmptySpliterator<Integer, Spliterator.OfInt, IntConsumer>
        implements Spliterator.OfInt {

      OfInt() {
      }
    }

    private static final class OfLong
        extends EmptySpliterator<Long, Spliterator.OfLong, LongConsumer>
        implements Spliterator.OfLong {

      OfLong() {
      }
    }

    private static final class OfDouble
        extends EmptySpliterator<Double, Spliterator.OfDouble, DoubleConsumer>
        implements Spliterator.OfDouble {

      OfDouble() {
      }
    }
  }

  // Array-based spliterators

  /**
   * A Spliterator designed for use by sources that traverse and split
   * elements maintained in an unmodifiable {@code Object[]} array.
   */
  static final class ArraySpliterator<T> implements Spliterator<T> {

    /**
     * The array, explicitly typed as Object[]. Unlike in some other
     * classes (see for example CR 6260652), we do not need to
     * screen arguments to ensure they are exactly of type Object[]
     * so long as no methods write into the array or serialize it,
     * which we ensure here by defining this class as final.
     */
    private final Object[] array;
    private int index;        // current index, modified on advance/split
    private final int fence;  // one past last index
    private final int characteristics;

    /**
     * Creates a spliterator covering all of the given array.
     *
     * @param array the array, assumed to be unmodified during use
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public ArraySpliterator(Object[] array, int additionalCharacteristics) {
      this(array, 0, array.length, additionalCharacteristics);
    }

    /**
     * Creates a spliterator covering the given array and range
     *
     * @param array the array, assumed to be unmodified during use
     * @param origin the least index (inclusive) to cover
     * @param fence one past the greatest index to cover
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public ArraySpliterator(Object[] array, int origin, int fence, int additionalCharacteristics) {
      this.array = array;
      this.index = origin;
      this.fence = fence;
      this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
    }

    @Override
    public Spliterator<T> trySplit() {
      int lo = index, mid = (lo + fence) >>> 1;
      return (lo >= mid)
          ? null
          : new ArraySpliterator<>(array, lo, index = mid, characteristics);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void forEachRemaining(Consumer<? super T> action) {
      Object[] a;
      int i, hi; // hoist accesses and checks from loop
      if (action == null) {
        throw new NullPointerException();
      }
      if ((a = array).length >= (hi = fence) &&
          (i = index) >= 0 && i < (index = hi)) {
        do {
          action.accept((T) a[i]);
        } while (++i < hi);
      }
    }

    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (index >= 0 && index < fence) {
        @SuppressWarnings("unchecked") T e = (T) array[index++];
        action.accept(e);
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return (long) (fence - index);
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super T> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  /**
   * A Spliterator.OfInt designed for use by sources that traverse and split
   * elements maintained in an unmodifiable {@code int[]} array.
   */
  static final class IntArraySpliterator implements Spliterator.OfInt {

    private final int[] array;
    private int index;        // current index, modified on advance/split
    private final int fence;  // one past last index
    private final int characteristics;

    /**
     * Creates a spliterator covering all of the given array.
     *
     * @param array the array, assumed to be unmodified during use
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public IntArraySpliterator(int[] array, int additionalCharacteristics) {
      this(array, 0, array.length, additionalCharacteristics);
    }

    /**
     * Creates a spliterator covering the given array and range
     *
     * @param array the array, assumed to be unmodified during use
     * @param origin the least index (inclusive) to cover
     * @param fence one past the greatest index to cover
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public IntArraySpliterator(int[] array, int origin, int fence, int additionalCharacteristics) {
      this.array = array;
      this.index = origin;
      this.fence = fence;
      this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
    }

    @Override
    public OfInt trySplit() {
      int lo = index, mid = (lo + fence) >>> 1;
      return (lo >= mid)
          ? null
          : new IntArraySpliterator(array, lo, index = mid, characteristics);
    }

    @Override
    public void forEachRemaining(IntConsumer action) {
      int[] a;
      int i, hi; // hoist accesses and checks from loop
      if (action == null) {
        throw new NullPointerException();
      }
      if ((a = array).length >= (hi = fence) &&
          (i = index) >= 0 && i < (index = hi)) {
        do {
          action.accept(a[i]);
        } while (++i < hi);
      }
    }

    @Override
    public boolean tryAdvance(IntConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (index >= 0 && index < fence) {
        action.accept(array[index++]);
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return (long) (fence - index);
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super Integer> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  /**
   * A Spliterator.OfLong designed for use by sources that traverse and split
   * elements maintained in an unmodifiable {@code int[]} array.
   */
  static final class LongArraySpliterator implements Spliterator.OfLong {

    private final long[] array;
    private int index;        // current index, modified on advance/split
    private final int fence;  // one past last index
    private final int characteristics;

    /**
     * Creates a spliterator covering all of the given array.
     *
     * @param array the array, assumed to be unmodified during use
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public LongArraySpliterator(long[] array, int additionalCharacteristics) {
      this(array, 0, array.length, additionalCharacteristics);
    }

    /**
     * Creates a spliterator covering the given array and range
     *
     * @param array the array, assumed to be unmodified during use
     * @param origin the least index (inclusive) to cover
     * @param fence one past the greatest index to cover
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public LongArraySpliterator(long[] array, int origin, int fence,
        int additionalCharacteristics) {
      this.array = array;
      this.index = origin;
      this.fence = fence;
      this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
    }

    @Override
    public OfLong trySplit() {
      int lo = index, mid = (lo + fence) >>> 1;
      return (lo >= mid)
          ? null
          : new LongArraySpliterator(array, lo, index = mid, characteristics);
    }

    @Override
    public void forEachRemaining(LongConsumer action) {
      long[] a;
      int i, hi; // hoist accesses and checks from loop
      if (action == null) {
        throw new NullPointerException();
      }
      if ((a = array).length >= (hi = fence) &&
          (i = index) >= 0 && i < (index = hi)) {
        do {
          action.accept(a[i]);
        } while (++i < hi);
      }
    }

    @Override
    public boolean tryAdvance(LongConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (index >= 0 && index < fence) {
        action.accept(array[index++]);
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return (long) (fence - index);
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super Long> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  /**
   * A Spliterator.OfDouble designed for use by sources that traverse and split
   * elements maintained in an unmodifiable {@code int[]} array.
   */
  static final class DoubleArraySpliterator implements Spliterator.OfDouble {

    private final double[] array;
    private int index;        // current index, modified on advance/split
    private final int fence;  // one past last index
    private final int characteristics;

    /**
     * Creates a spliterator covering all of the given array.
     *
     * @param array the array, assumed to be unmodified during use
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public DoubleArraySpliterator(double[] array, int additionalCharacteristics) {
      this(array, 0, array.length, additionalCharacteristics);
    }

    /**
     * Creates a spliterator covering the given array and range
     *
     * @param array the array, assumed to be unmodified during use
     * @param origin the least index (inclusive) to cover
     * @param fence one past the greatest index to cover
     * @param additionalCharacteristics Additional spliterator characteristics of this spliterator's
     * source or elements beyond {@code SIZED} and {@code SUBSIZED} which are are always reported
     */
    public DoubleArraySpliterator(double[] array, int origin, int fence,
        int additionalCharacteristics) {
      this.array = array;
      this.index = origin;
      this.fence = fence;
      this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
    }

    @Override
    public OfDouble trySplit() {
      int lo = index, mid = (lo + fence) >>> 1;
      return (lo >= mid)
          ? null
          : new DoubleArraySpliterator(array, lo, index = mid, characteristics);
    }

    @Override
    public void forEachRemaining(DoubleConsumer action) {
      double[] a;
      int i, hi; // hoist accesses and checks from loop
      if (action == null) {
        throw new NullPointerException();
      }
      if ((a = array).length >= (hi = fence) &&
          (i = index) >= 0 && i < (index = hi)) {
        do {
          action.accept(a[i]);
        } while (++i < hi);
      }
    }

    @Override
    public boolean tryAdvance(DoubleConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (index >= 0 && index < fence) {
        action.accept(array[index++]);
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return (long) (fence - index);
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super Double> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  //

  /**
   * An abstract {@code Spliterator} that implements {@code trySplit} to
   * permit limited parallelism.
   *
   * <p>An extending class need only
   * implement {@link #tryAdvance(java.util.function.Consumer) tryAdvance}.
   * The extending class should override
   * {@link #forEachRemaining(java.util.function.Consumer) forEach} if it can
   * provide a more performant implementation.
   *
   * @apiNote This class is a useful aid for creating a spliterator when it is not possible or
   * difficult to efficiently partition elements in a manner allowing balanced parallel
   * computation.
   *
   * <p>An alternative to using this class, that also permits limited parallelism, is to create a
   * spliterator from an iterator (see {@link #spliterator(Iterator, long, int)}.  Depending on the
   * circumstances using an iterator may be easier or more convenient than extending this class,
   * such as when there is already an iterator available to use.
   * @see #spliterator(Iterator, long, int)
   * @since 1.8
   */
  public static abstract class AbstractSpliterator<T> implements Spliterator<T> {

    static final int BATCH_UNIT = 1 << 10;  // batch array size increment
    static final int MAX_BATCH = 1 << 25;  // max batch array size;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator reporting the given estimated size and
     * additionalCharacteristics.
     *
     * @param est the estimated size of this spliterator if known, otherwise {@code
     * Long.MAX_VALUE}.
     * @param additionalCharacteristics properties of this spliterator's source or elements.  If
     * {@code SIZED} is reported then this spliterator will additionally report {@code SUBSIZED}.
     */
    protected AbstractSpliterator(long est, int additionalCharacteristics) {
      this.est = est;
      this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
          ? additionalCharacteristics | Spliterator.SUBSIZED
          : additionalCharacteristics;
    }

    static final class HoldingConsumer<T> implements Consumer<T> {

      Object value;

      @Override
      public void accept(T value) {
        this.value = value;
      }
    }

    /**
     * {@inheritDoc}
     *
     * This implementation permits limited parallelism.
     */
    @Override
    public Spliterator<T> trySplit() {
            /*
             * Split into arrays of arithmetically increasing batch
             * sizes.  This will only improve parallel performance if
             * per-element Consumer actions are more costly than
             * transferring them into an array.  The use of an
             * arithmetic progression in split sizes provides overhead
             * vs parallelism bounds that do not particularly favor or
             * penalize cases of lightweight vs heavyweight element
             * operations, across combinations of #elements vs #cores,
             * whether or not either are known.  We generate
             * O(sqrt(#elements)) splits, allowing O(sqrt(#cores))
             * potential speedup.
             */
      HoldingConsumer<T> holder = new HoldingConsumer<>();
      long s = est;
      if (s > 1 && tryAdvance(holder)) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        Object[] a = new Object[n];
        int j = 0;
        do {
          a[j] = holder.value;
        } while (++j < n && tryAdvance(holder));
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new ArraySpliterator<>(a, 0, j, characteristics());
      }
      return null;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the estimated size as reported when created and, if the
     * estimate size is known, decreases in size when split.
     */
    @Override
    public long estimateSize() {
      return est;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the characteristics as reported when created.
     */
    @Override
    public int characteristics() {
      return characteristics;
    }
  }

  /**
   * An abstract {@code Spliterator.OfInt} that implements {@code trySplit} to
   * permit limited parallelism.
   *
   * <p>To implement a spliterator an extending class need only
   * implement {@link #tryAdvance(java.util.function.IntConsumer)}
   * tryAdvance}.  The extending class should override
   * {@link #forEachRemaining(java.util.function.IntConsumer)} forEach} if it
   * can provide a more performant implementation.
   *
   * @apiNote This class is a useful aid for creating a spliterator when it is not possible or
   * difficult to efficiently partition elements in a manner allowing balanced parallel
   * computation.
   *
   * <p>An alternative to using this class, that also permits limited parallelism, is to create a
   * spliterator from an iterator (see {@link #spliterator(java.util.PrimitiveIterator.OfInt, long,
   * int)}. Depending on the circumstances using an iterator may be easier or more convenient than
   * extending this class. For example, if there is already an iterator available to use then there
   * is no need to extend this class.
   * @see #spliterator(java.util.PrimitiveIterator.OfInt, long, int)
   * @since 1.8
   */
  public static abstract class AbstractIntSpliterator implements Spliterator.OfInt {

    static final int MAX_BATCH = AbstractSpliterator.MAX_BATCH;
    static final int BATCH_UNIT = AbstractSpliterator.BATCH_UNIT;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator reporting the given estimated size and
     * characteristics.
     *
     * @param est the estimated size of this spliterator if known, otherwise {@code
     * Long.MAX_VALUE}.
     * @param additionalCharacteristics properties of this spliterator's source or elements.  If
     * {@code SIZED} is reported then this spliterator will additionally report {@code SUBSIZED}.
     */
    protected AbstractIntSpliterator(long est, int additionalCharacteristics) {
      this.est = est;
      this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
          ? additionalCharacteristics | Spliterator.SUBSIZED
          : additionalCharacteristics;
    }

    static final class HoldingIntConsumer implements IntConsumer {

      int value;

      @Override
      public void accept(int value) {
        this.value = value;
      }
    }

    /**
     * {@inheritDoc}
     *
     * This implementation permits limited parallelism.
     */
    @Override
    public Spliterator.OfInt trySplit() {
      HoldingIntConsumer holder = new HoldingIntConsumer();
      long s = est;
      if (s > 1 && tryAdvance(holder)) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        int[] a = new int[n];
        int j = 0;
        do {
          a[j] = holder.value;
        } while (++j < n && tryAdvance(holder));
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new IntArraySpliterator(a, 0, j, characteristics());
      }
      return null;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the estimated size as reported when created and, if the
     * estimate size is known, decreases in size when split.
     */
    @Override
    public long estimateSize() {
      return est;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the characteristics as reported when created.
     */
    @Override
    public int characteristics() {
      return characteristics;
    }
  }

  /**
   * An abstract {@code Spliterator.OfLong} that implements {@code trySplit}
   * to permit limited parallelism.
   *
   * <p>To implement a spliterator an extending class need only
   * implement {@link #tryAdvance(java.util.function.LongConsumer)}
   * tryAdvance}.  The extending class should override
   * {@link #forEachRemaining(java.util.function.LongConsumer)} forEach} if it
   * can provide a more performant implementation.
   *
   * @apiNote This class is a useful aid for creating a spliterator when it is not possible or
   * difficult to efficiently partition elements in a manner allowing balanced parallel
   * computation.
   *
   * <p>An alternative to using this class, that also permits limited parallelism, is to create a
   * spliterator from an iterator (see {@link #spliterator(java.util.PrimitiveIterator.OfLong, long,
   * int)}. Depending on the circumstances using an iterator may be easier or more convenient than
   * extending this class. For example, if there is already an iterator available to use then there
   * is no need to extend this class.
   * @see #spliterator(java.util.PrimitiveIterator.OfLong, long, int)
   * @since 1.8
   */
  public static abstract class AbstractLongSpliterator implements Spliterator.OfLong {

    static final int MAX_BATCH = AbstractSpliterator.MAX_BATCH;
    static final int BATCH_UNIT = AbstractSpliterator.BATCH_UNIT;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator reporting the given estimated size and
     * characteristics.
     *
     * @param est the estimated size of this spliterator if known, otherwise {@code
     * Long.MAX_VALUE}.
     * @param additionalCharacteristics properties of this spliterator's source or elements.  If
     * {@code SIZED} is reported then this spliterator will additionally report {@code SUBSIZED}.
     */
    protected AbstractLongSpliterator(long est, int additionalCharacteristics) {
      this.est = est;
      this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
          ? additionalCharacteristics | Spliterator.SUBSIZED
          : additionalCharacteristics;
    }

    static final class HoldingLongConsumer implements LongConsumer {

      long value;

      @Override
      public void accept(long value) {
        this.value = value;
      }
    }

    /**
     * {@inheritDoc}
     *
     * This implementation permits limited parallelism.
     */
    @Override
    public Spliterator.OfLong trySplit() {
      HoldingLongConsumer holder = new HoldingLongConsumer();
      long s = est;
      if (s > 1 && tryAdvance(holder)) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        long[] a = new long[n];
        int j = 0;
        do {
          a[j] = holder.value;
        } while (++j < n && tryAdvance(holder));
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new LongArraySpliterator(a, 0, j, characteristics());
      }
      return null;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the estimated size as reported when created and, if the
     * estimate size is known, decreases in size when split.
     */
    @Override
    public long estimateSize() {
      return est;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the characteristics as reported when created.
     */
    @Override
    public int characteristics() {
      return characteristics;
    }
  }

  /**
   * An abstract {@code Spliterator.OfDouble} that implements
   * {@code trySplit} to permit limited parallelism.
   *
   * <p>To implement a spliterator an extending class need only
   * implement {@link #tryAdvance(java.util.function.DoubleConsumer)}
   * tryAdvance}.  The extending class should override
   * {@link #forEachRemaining(java.util.function.DoubleConsumer)} forEach} if
   * it can provide a more performant implementation.
   *
   * @apiNote This class is a useful aid for creating a spliterator when it is not possible or
   * difficult to efficiently partition elements in a manner allowing balanced parallel
   * computation.
   *
   * <p>An alternative to using this class, that also permits limited parallelism, is to create a
   * spliterator from an iterator (see {@link #spliterator(java.util.PrimitiveIterator.OfDouble,
   * long, int)}. Depending on the circumstances using an iterator may be easier or more convenient
   * than extending this class. For example, if there is already an iterator available to use then
   * there is no need to extend this class.
   * @see #spliterator(java.util.PrimitiveIterator.OfDouble, long, int)
   * @since 1.8
   */
  public static abstract class AbstractDoubleSpliterator implements Spliterator.OfDouble {

    static final int MAX_BATCH = AbstractSpliterator.MAX_BATCH;
    static final int BATCH_UNIT = AbstractSpliterator.BATCH_UNIT;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator reporting the given estimated size and
     * characteristics.
     *
     * @param est the estimated size of this spliterator if known, otherwise {@code
     * Long.MAX_VALUE}.
     * @param additionalCharacteristics properties of this spliterator's source or elements.  If
     * {@code SIZED} is reported then this spliterator will additionally report {@code SUBSIZED}.
     */
    protected AbstractDoubleSpliterator(long est, int additionalCharacteristics) {
      this.est = est;
      this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)
          ? additionalCharacteristics | Spliterator.SUBSIZED
          : additionalCharacteristics;
    }

    static final class HoldingDoubleConsumer implements DoubleConsumer {

      double value;

      @Override
      public void accept(double value) {
        this.value = value;
      }
    }

    /**
     * {@inheritDoc}
     *
     * This implementation permits limited parallelism.
     */
    @Override
    public Spliterator.OfDouble trySplit() {
      HoldingDoubleConsumer holder = new HoldingDoubleConsumer();
      long s = est;
      if (s > 1 && tryAdvance(holder)) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        double[] a = new double[n];
        int j = 0;
        do {
          a[j] = holder.value;
        } while (++j < n && tryAdvance(holder));
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new DoubleArraySpliterator(a, 0, j, characteristics());
      }
      return null;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the estimated size as reported when created and, if the
     * estimate size is known, decreases in size when split.
     */
    @Override
    public long estimateSize() {
      return est;
    }

    /**
     * {@inheritDoc}
     *
     * @implSpec This implementation returns the characteristics as reported when created.
     */
    @Override
    public int characteristics() {
      return characteristics;
    }
  }

  // Iterator-based Spliterators

  /**
   * A Spliterator using a given Iterator for element
   * operations. The spliterator implements {@code trySplit} to
   * permit limited parallelism.
   */
  static class IteratorSpliterator<T> implements Spliterator<T> {

    static final int BATCH_UNIT = 1 << 10;  // batch array size increment
    static final int MAX_BATCH = 1 << 25;  // max batch array size;
    private final Collection<? extends T> collection; // null OK
    private Iterator<? extends T> it;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator using the given given
     * collection's {@link java.util.Collection#iterator()) for traversal,
     * and reporting its {@link java.util.Collection#size()) as its initial
     * size.
     *
     * @param c the collection
     * @param characteristics properties of this spliterator's source or elements.
     */
    public IteratorSpliterator(Collection<? extends T> collection, int characteristics) {
      this.collection = collection;
      this.it = null;
      this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
          ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
          : characteristics;
    }

    /**
     * Creates a spliterator using the given iterator
     * for traversal, and reporting the given initial size
     * and characteristics.
     *
     * @param iterator the iterator for the source
     * @param size the number of elements in the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public IteratorSpliterator(Iterator<? extends T> iterator, long size, int characteristics) {
      this.collection = null;
      this.it = iterator;
      this.est = size;
      this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
          ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
          : characteristics;
    }

    /**
     * Creates a spliterator using the given iterator
     * for traversal, and reporting the given initial size
     * and characteristics.
     *
     * @param iterator the iterator for the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public IteratorSpliterator(Iterator<? extends T> iterator, int characteristics) {
      this.collection = null;
      this.it = iterator;
      this.est = Long.MAX_VALUE;
      this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
    }

    @Override
    public Spliterator<T> trySplit() {
            /*
             * Split into arrays of arithmetically increasing batch
             * sizes.  This will only improve parallel performance if
             * per-element Consumer actions are more costly than
             * transferring them into an array.  The use of an
             * arithmetic progression in split sizes provides overhead
             * vs parallelism bounds that do not particularly favor or
             * penalize cases of lightweight vs heavyweight element
             * operations, across combinations of #elements vs #cores,
             * whether or not either are known.  We generate
             * O(sqrt(#elements)) splits, allowing O(sqrt(#cores))
             * potential speedup.
             */
      Iterator<? extends T> i;
      long s;
      if ((i = it) == null) {
        i = it = collection.iterator();
        s = est = (long) collection.size();
      } else {
        s = est;
      }
      if (s > 1 && i.hasNext()) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        Object[] a = new Object[n];
        int j = 0;
        do {
          a[j] = i.next();
        } while (++j < n && i.hasNext());
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new ArraySpliterator<>(a, 0, j, characteristics);
      }
      return null;
    }

    @Override
    public void forEachRemaining(Consumer<? super T> action) {
      if (action == null) {
        throw new NullPointerException();
      }
      Iterator<? extends T> i;
      if ((i = it) == null) {
        i = it = collection.iterator();
        est = (long) collection.size();
      }
      i.forEachRemaining(action);
    }

    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (it == null) {
        it = collection.iterator();
        est = (long) collection.size();
      }
      if (it.hasNext()) {
        action.accept(it.next());
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      if (it == null) {
        it = collection.iterator();
        return est = (long) collection.size();
      }
      return est;
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super T> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  /**
   * A Spliterator.OfInt using a given IntStream.IntIterator for element
   * operations. The spliterator implements {@code trySplit} to
   * permit limited parallelism.
   */
  static final class IntIteratorSpliterator implements Spliterator.OfInt {

    static final int BATCH_UNIT = IteratorSpliterator.BATCH_UNIT;
    static final int MAX_BATCH = IteratorSpliterator.MAX_BATCH;
    private PrimitiveIterator.OfInt it;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator using the given iterator
     * for traversal, and reporting the given initial size
     * and characteristics.
     *
     * @param iterator the iterator for the source
     * @param size the number of elements in the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public IntIteratorSpliterator(PrimitiveIterator.OfInt iterator, long size,
        int characteristics) {
      this.it = iterator;
      this.est = size;
      this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
          ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
          : characteristics;
    }

    /**
     * Creates a spliterator using the given iterator for a
     * source of unknown size, reporting the given
     * characteristics.
     *
     * @param iterator the iterator for the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public IntIteratorSpliterator(PrimitiveIterator.OfInt iterator, int characteristics) {
      this.it = iterator;
      this.est = Long.MAX_VALUE;
      this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
    }

    @Override
    public OfInt trySplit() {
      PrimitiveIterator.OfInt i = it;
      long s = est;
      if (s > 1 && i.hasNext()) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        int[] a = new int[n];
        int j = 0;
        do {
          a[j] = i.nextInt();
        } while (++j < n && i.hasNext());
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new IntArraySpliterator(a, 0, j, characteristics);
      }
      return null;
    }

    @Override
    public void forEachRemaining(IntConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      it.forEachRemaining(action);
    }

    @Override
    public boolean tryAdvance(IntConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (it.hasNext()) {
        action.accept(it.nextInt());
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return est;
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super Integer> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  static final class LongIteratorSpliterator implements Spliterator.OfLong {

    static final int BATCH_UNIT = IteratorSpliterator.BATCH_UNIT;
    static final int MAX_BATCH = IteratorSpliterator.MAX_BATCH;
    private PrimitiveIterator.OfLong it;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator using the given iterator
     * for traversal, and reporting the given initial size
     * and characteristics.
     *
     * @param iterator the iterator for the source
     * @param size the number of elements in the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public LongIteratorSpliterator(PrimitiveIterator.OfLong iterator, long size,
        int characteristics) {
      this.it = iterator;
      this.est = size;
      this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
          ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
          : characteristics;
    }

    /**
     * Creates a spliterator using the given iterator for a
     * source of unknown size, reporting the given
     * characteristics.
     *
     * @param iterator the iterator for the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public LongIteratorSpliterator(PrimitiveIterator.OfLong iterator, int characteristics) {
      this.it = iterator;
      this.est = Long.MAX_VALUE;
      this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
    }

    @Override
    public OfLong trySplit() {
      PrimitiveIterator.OfLong i = it;
      long s = est;
      if (s > 1 && i.hasNext()) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        long[] a = new long[n];
        int j = 0;
        do {
          a[j] = i.nextLong();
        } while (++j < n && i.hasNext());
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new LongArraySpliterator(a, 0, j, characteristics);
      }
      return null;
    }

    @Override
    public void forEachRemaining(LongConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      it.forEachRemaining(action);
    }

    @Override
    public boolean tryAdvance(LongConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (it.hasNext()) {
        action.accept(it.nextLong());
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return est;
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super Long> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }

  static final class DoubleIteratorSpliterator implements Spliterator.OfDouble {

    static final int BATCH_UNIT = IteratorSpliterator.BATCH_UNIT;
    static final int MAX_BATCH = IteratorSpliterator.MAX_BATCH;
    private PrimitiveIterator.OfDouble it;
    private final int characteristics;
    private long est;             // size estimate
    private int batch;            // batch size for splits

    /**
     * Creates a spliterator using the given iterator
     * for traversal, and reporting the given initial size
     * and characteristics.
     *
     * @param iterator the iterator for the source
     * @param size the number of elements in the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public DoubleIteratorSpliterator(PrimitiveIterator.OfDouble iterator, long size,
        int characteristics) {
      this.it = iterator;
      this.est = size;
      this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
          ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
          : characteristics;
    }

    /**
     * Creates a spliterator using the given iterator for a
     * source of unknown size, reporting the given
     * characteristics.
     *
     * @param iterator the iterator for the source
     * @param characteristics properties of this spliterator's source or elements.
     */
    public DoubleIteratorSpliterator(PrimitiveIterator.OfDouble iterator, int characteristics) {
      this.it = iterator;
      this.est = Long.MAX_VALUE;
      this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
    }

    @Override
    public OfDouble trySplit() {
      PrimitiveIterator.OfDouble i = it;
      long s = est;
      if (s > 1 && i.hasNext()) {
        int n = batch + BATCH_UNIT;
        if (n > s) {
          n = (int) s;
        }
        if (n > MAX_BATCH) {
          n = MAX_BATCH;
        }
        double[] a = new double[n];
        int j = 0;
        do {
          a[j] = i.nextDouble();
        } while (++j < n && i.hasNext());
        batch = j;
        if (est != Long.MAX_VALUE) {
          est -= j;
        }
        return new DoubleArraySpliterator(a, 0, j, characteristics);
      }
      return null;
    }

    @Override
    public void forEachRemaining(DoubleConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      it.forEachRemaining(action);
    }

    @Override
    public boolean tryAdvance(DoubleConsumer action) {
      if (action == null) {
        throw new NullPointerException();
      }
      if (it.hasNext()) {
        action.accept(it.nextDouble());
        return true;
      }
      return false;
    }

    @Override
    public long estimateSize() {
      return est;
    }

    @Override
    public int characteristics() {
      return characteristics;
    }

    @Override
    public Comparator<? super Double> getComparator() {
      if (hasCharacteristics(Spliterator.SORTED)) {
        return null;
      }
      throw new IllegalStateException();
    }
  }
}
